/*********************************************************************
*
* Software License Agreement (BSD License)
*
*  Copyright (c) 2010, Heriot-Watt University, UK.
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the Heriot-Watt University nor the names of 
*     its contributors may be used to endorse or promote products 
*     derived from this software without specific prior written
*     permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*
*  Author: Joel Cartwright, Josep Bosch
*
*********************************************************************/

#ifndef _OSLCORE_MODULECORE_H_
#define _OSLCORE_MODULECORE_H_

// ================================================================== includes
#include <iostream>
#include <iomanip>
#include <string>

#include <cc++/config.h>
#include <cc++/thread.h>

#include <ros/ros.h>
#include <matchmaker/client.h>

#include <osl_core/ProcessWatcher.h>
#include <osl_core/GeneralMath.h>
#include <osl_core/ConfigWrapper.h>
#include <osl_core/TimerMillis.h>

// ========================================================== external defines

// ---------------------------------------------------------------------------
// Text display macros.
// For best practice on usage, see http://www.ros.org/wiki/Verbosity%20Levels
// ---------------------------------------------------------------------------

// Debug is off by default, but can be enabled at runtime using rxconsole.
// DEBUG_OUTPUT macro has no effect on PRINTLN_DEBUG when using ROS.
#define PRINTLN_DEBUG(x)	ROS_DEBUG_STREAM(x)

// Info is for things like module start up messages.
#define PRINTLN_INFO(x)		ROS_INFO_STREAM(x)

// Warn is for unexpected or slightly bad events.
#define PRINTLN_WARN(x)		ROS_WARN_STREAM(x)

// Err is for bad events.
#define PRINTLN_ERR(x)		ROS_ERROR_STREAM(x)

// Err is for critical, non-recoverable events.
#define PRINTLN_FATAL(x)	ROS_FATAL_STREAM(x)


// ===================================================================== class

namespace osl_core
{

/**
 * Modulde core class, to be sub-classed by all modules.
 */
class ModuleCore : public ost::Thread
{
public:
	static const int DEFAULT_MIN_WAIT_TIME = 10;
	static const int DEFAULT_MAX_WAIT_TIME = 100;
	static const unsigned MAX_OSH_MSGS_PER_LOOP = 100;

	ModuleCore();

	virtual ~ModuleCore();

	int main(int argc, char * argv[], bool noWatcher=false);

	// coreInit is called by run, and does one-time initialisation.
	bool coreInit();

	// Cleans up the core.
	void coreCleanup();

	/**
	 * Clean msg queue or wait for the queue until timeout
	 */
	bool coreHandleMsgLoop (const timeout_t & waitTimeout = DEFAULT_MAX_WAIT_TIME);

	void setMinWaitTime(timeout_t waitTimeMs) { if (waitTimeMs > 0) m_minWaitTime = waitTimeMs; };

	timeout_t getMinWaitTime() { return m_minWaitTime; };

	void requestShutdown();

	void requestRestart();

	bool isStopping() { return m_stopRequested; };

	bool isStandaloneProcess() { return m_standaloneProcessMode; };

	std::string getModuleName() { return m_moduleName; };

	static std::string currentTimeString();

	static std::string createROSNodeName(const std::string& moduleName);

	static bool checkROSClock();

protected:
	void run(void);

 	// Virtual functions, to be overriden by subclass.
	/**
	 * Custom module initialisation code (pure virtual, must be overriden).
	 * Returns false on failure, true otherwise.
	 */
	virtual bool init(int argc, char * argv[]) = 0;

	/**
	 * Main work of the module is perfomed here, other than message
	 * handling (pure virtual, must be overriden).
	 */
	virtual void doWork() = 0;
	/**
	 * Custom module cleanup (pure virtual, must be overriden).
	 */
	virtual void cleanup() = 0;

	static std::string olibINIParamToROSParam(const std::string &name, const std::string &section);

	/**
	 * Gets an integer from the ini file.
	 * Prints an error message on failure.
	 *
	 * @return false on failure, true otherwise
	 */
	bool getINIInt(int &var, const std::string &name, const std::string &section);

	/**
	 * Gets a float from the ini file.
	 * Prints an error message on failure.
	 *
	 * @return false on failure, true otherwise
	 */
	bool getINIFloat(float &var, const std::string &name, const std::string &section);

	/**
	 * Gets a string from the ini file.
	 * Prints an error message on failure.
	 *
	 * @return false on failure, true otherwise
	 */
	bool getINIString(std::string &var, const std::string &name, const std::string &section);

	/**
	 * Gets a boolean from the ini file.
	 * Prints an error message on failure.
	 *
	 * @return false on failure, true otherwise
	 */
	bool getINIBool(bool &var, const std::string &name, const std::string &section);

	/**
	 * Returns true if OceanSHELL reception and sending is enabled.
	 * If disabled, handleMsg will never be called, and sendMsg will do nothing.
	 */
	bool isOshEnabled();

	ros::NodeHandlePtr m_rosHandle;

	ros::TransportHints m_rosHints;

	bool m_useSoa;

	matchmaker::ClientPtr m_soaClient;

	osl_core::ConfigWrapper m_conf;

	bool m_standaloneProcessMode;

private:
	static void signalHandler(int sig);

	/**
	 * Installs the signal handlers for the core.
	 */
	static void initSignals();

	/**
	 * Prints a warning about a missing INI parameter.
	 *
	 */
	inline void printNoINIParam(const std::string &name, const std::string &section);

	bool		m_initCalled;

	std::string	m_diagPrefix;

	bool		m_stopRequested;
	int			m_exitValue;

	bool		m_bDaemonised;
	std::string	m_logFile;
	std::string	m_lockFile;

	timeout_t	m_minWaitTime;

	std::string	m_moduleName;

	// Program arguments, saved for thread.
	int			m_argc;
	char **		m_argv;

	// Static variables only below here.

	static bool runOnce;

	static ModuleCore *activeCore;

	static std::string iniFilename;
};

} // namespace

#endif
